// DriveNotes. Copyright (c) 2011, Andrei Senchuk. All rights reserved.
package net.taviscaron.drivenotes.web.action;

import com.googlecode.charts4j.AxisLabels;
import com.googlecode.charts4j.AxisLabelsFactory;
import com.googlecode.charts4j.AxisStyle;
import com.googlecode.charts4j.AxisTextAlignment;
import com.googlecode.charts4j.BarChart;
import com.googlecode.charts4j.BarChartPlot;
import com.googlecode.charts4j.Color;
import com.googlecode.charts4j.Data;
import com.googlecode.charts4j.DataUtil;
import com.googlecode.charts4j.Fills;
import com.googlecode.charts4j.GCharts;
import com.googlecode.charts4j.PieChart;
import com.googlecode.charts4j.Plot;
import com.googlecode.charts4j.Plots;
import com.googlecode.charts4j.Slice;
import com.googlecode.charts4j.XYLineChart;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.taviscaron.drivenotes.shared.model.entity.Car;
import net.taviscaron.drivenotes.shared.model.entity.User;
import net.taviscaron.drivenotes.shared.service.GarageService;
import net.taviscaron.drivenotes.shared.service.StatisticService;
import net.taviscaron.drivenotes.web.core.Constants;
import net.taviscaron.drivenotes.web.forward.RequestForwarder;
import net.taviscaron.drivenotes.web.forward.ViewForward;
import net.taviscaron.drivenotes.web.util.ColorUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

/**
 * Statistic action
 * @author Andrei Senchuk
 */
public class StatisticAction implements Action {
    private static final Log logger = LogFactory.getLog(StatisticAction.class);
    private static final float CHART_MAX_COEFFICIENT = 1.2f;
    private static final int BAR_WIDTH = 50;
    private static final int SPACE_WIDTH = 20;
    private static final int BAR_CHART_WIDTH = 500;
    private static final int PIE_CHART_WIDTH = 500;
    private static final int PIE_CHART_HEIGHT = 200;
    private static final int XY_CHART_WIDTH = 500;
    private static final int XY_CHART_HEIGHT = 300;
    private static final int XY_CHART_Y_AXIS_LABELS = 10;
    private static final Color BACKGROUND_COLOR = Color.newColor("050402");

    private GarageService garageService;
    private StatisticService statisticService;

    @Required
    public void setGarageService(GarageService garageService) {
        this.garageService = garageService;
    }

    @Required
    public void setStatisticService(StatisticService statisticService) {
        this.statisticService = statisticService;
    }

    public RequestForwarder doAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        User user = (User)request.getAttribute(Constants.AUTHORIZED_USER_ATTR_NAME);
        List<Car> cars = garageService.findCarsByOwnerId(user.getId());

        Map<Car, String> carTotalCostCircleChart = new HashMap<Car, String>();
        Map<Car, String> carFuelConsumptionTendention = new HashMap<Car, String>();
        if(!cars.isEmpty()) {
            request.setAttribute("cars", cars);

            String totalServiceConstChart = totalServiceCostChart(cars);
            request.setAttribute("totalServiceCostChart", totalServiceConstChart);

            String totalChargingCostChart = totalChargingCostChart(cars);
            request.setAttribute("totalChargingCostChart", totalChargingCostChart);

            for(Car car : cars) {
                carTotalCostCircleChart.put(car, totalCostCircleChart(car));
                carFuelConsumptionTendention.put(car, fuelConsumptionTendention(car));
            }

            request.setAttribute("carTotalCostCircleChart", carTotalCostCircleChart);
            request.setAttribute("carFuelConsumptionTendention", carFuelConsumptionTendention);
        }
        return new ViewForward("statistic");
    }

    private String totalServiceCostChart(List<Car> cars) {
        Map<Car, Float> costs = new HashMap<Car, Float>();
        float max = 0;
        int chartHeight = 20;

        for(Car car : cars) {
            Float cost = statisticService.calculateTotalServiceCost(car);
            costs.put(car, cost);
            if(cost > max) {
                max = cost;
            }
        }

        max *= CHART_MAX_COEFFICIENT;

        List<BarChartPlot> plots = new ArrayList<BarChartPlot>(costs.size());
        for(Car car : cars) {
            Double scaledCost = costs.get(car) / max * Data.MAX_VALUE;
            plots.add(Plots.newBarChartPlot(Data.newData(scaledCost), ColorUtils.nextColor(), car.getBrand() + " " + car.getModel()));
            chartHeight += BAR_WIDTH + SPACE_WIDTH;
        }

        BarChart chart = GCharts.newBarChart(plots);

        chart.addXAxisLabels(AxisLabelsFactory.newNumericRangeAxisLabels(0, max));
        
        chart.setSize(BAR_CHART_WIDTH, chartHeight);
        chart.setBarWidth(BAR_WIDTH);
        chart.setHorizontal(true);
        chart.setSpaceWithinGroupsOfBars(SPACE_WIDTH);
        chart.setGrid(10, 100, 3, 2);
        chart.setBackgroundFill(Fills.newSolidFill(BACKGROUND_COLOR));

        return chart.toURLForHTML();
    }

    private String totalChargingCostChart(List<Car> cars) {
        Map<Car, Float> costs = new HashMap<Car, Float>();
        float max = 0;
        int chartHeight = 20;

        for(Car car : cars) {
            Float cost = statisticService.calculateTotalChargingCost(car);
            costs.put(car, cost);
            if(cost > max) {
                max = cost;
            }
        }

        max *= CHART_MAX_COEFFICIENT;

        List<BarChartPlot> plots = new ArrayList<BarChartPlot>(costs.size());
        for(Car car : cars) {
            Double scaledCost = costs.get(car) / max * Data.MAX_VALUE;
            plots.add(Plots.newBarChartPlot(Data.newData(scaledCost), ColorUtils.nextColor(), car.getBrand() + " " + car.getModel()));
            chartHeight += BAR_WIDTH + SPACE_WIDTH;
        }

        BarChart chart = GCharts.newBarChart(plots);

        chart.addXAxisLabels(AxisLabelsFactory.newNumericRangeAxisLabels(0, max));

        chart.setSize(BAR_CHART_WIDTH, chartHeight);
        chart.setBarWidth(BAR_WIDTH);
        chart.setHorizontal(true);
        chart.setSpaceWithinGroupsOfBars(SPACE_WIDTH);
        chart.setGrid(10, 100, 3, 2);
        chart.setBackgroundFill(Fills.newSolidFill(BACKGROUND_COLOR));

        return chart.toURLForHTML();
    }

    private String totalCostCircleChart(Car car) {
        Float totalChargingCost = statisticService.calculateTotalChargingCost(car);
        Float totalServiceCost = statisticService.calculateTotalServiceCost(car);
        Float totalCost = totalChargingCost + totalServiceCost;

        if (totalCost == 0) {
            return null;
        }

        int chargingCostPart = (int)(totalChargingCost / totalCost * 100);
        int serviceCostPart = 100 - chargingCostPart;

        List<Slice> slices = new ArrayList<Slice>(2);

        if(chargingCostPart > 0) {
            slices.add(Slice.newSlice(chargingCostPart, ColorUtils.nextColor(), "Charging (" + chargingCostPart + "%)"));
        }

        if(serviceCostPart > 0) {
            slices.add(Slice.newSlice(serviceCostPart, ColorUtils.nextColor(), "Service (" + serviceCostPart + "%)"));
        }

        PieChart chart = GCharts.newPieChart(slices);
        chart.setSize(PIE_CHART_WIDTH, PIE_CHART_HEIGHT);
        chart.setThreeD(true);
        chart.setBackgroundFill(Fills.newSolidFill(BACKGROUND_COLOR));

        return chart.toURLForHTML();
    }

    private String fuelConsumptionTendention(Car car) {
        TreeMap<Integer, Double> fuelConsumptionTendention = statisticService.calculateFuelConsumptionTendention(car);
        if(fuelConsumptionTendention == null || fuelConsumptionTendention.isEmpty()) {
            return null;
        }

        List<Integer> mileage = new ArrayList<Integer>(fuelConsumptionTendention.keySet());
        List<Double> consumption = new ArrayList<Double>(fuelConsumptionTendention.values());

        double chartMax = Collections.max(consumption) * CHART_MAX_COEFFICIENT;

        Plot plot = Plots.newPlot(DataUtil.scaleWithinRange(0, chartMax, consumption));
        XYLineChart chart = GCharts.newXYLineChart(plot);
        chart.addXAxisLabels(AxisLabelsFactory.newNumericAxisLabels(mileage.get(0),
                mileage.get(mileage.size() / 2), mileage.get(mileage.size() - 1)));
        chart.addYAxisLabels(AxisLabelsFactory.newNumericRangeAxisLabels(0, chartMax, chartMax / XY_CHART_Y_AXIS_LABELS));
        chart.setSize(XY_CHART_WIDTH, XY_CHART_HEIGHT);
        chart.setGrid(100, 100.0 / XY_CHART_Y_AXIS_LABELS, 3, 2);
        chart.setBackgroundFill(Fills.newSolidFill(BACKGROUND_COLOR));

        return chart.toURLForHTML();
    }
}
